﻿#include "../../src/detail/csv_reader_impl.hh"
#include "../../src/util/os_util.hh"
#include "../../src/util/time_util.hh"
#include "../framework/unittest.hh"
#include <fstream>

FIXTURE_BEGIN(test_csv)

using namespace kratos;

SETUP([]() {
  std::ofstream ofs;
  ofs.open("test.csv", std::ios::out | std::ios::trunc);
  ofs << "a,b,c\r\n" << std::endl;
  ofs << "d,e,f\r\n" << std::endl;
  ofs.close();

  std::ofstream ofs1;
  ofs1.open("test1.csv", std::ios::out | std::ios::trunc);
  ofs1 << "a,b,c\r\n" << std::endl;
  ofs1 << "d,e,f" << std::endl;
  ofs1.close();

  std::ofstream ofs2;
  ofs2.open("test2.csv", std::ios::out | std::ios::trunc);
  ofs2 << "a,  b,c  \r\n" << std::endl;
  ofs2 << "  d,e  ,  f" << std::endl;
  ofs2.close();

  std::ofstream ofs3;
  ofs3.open("test3.csv", std::ios::out | std::ios::trunc);
  ofs3 << "  \"a\", b , \"c\"   \r\n" << std::endl;
  ofs3 << "  d,  \"e\"  ,f  \r\n" << std::endl;
  ofs3.close();

  std::ofstream ofs4;
  ofs4.open("test4.csv", std::ios::out | std::ios::trunc);
  ofs4 << "  \" a \", b , \" c \"   \r\n" << std::endl;
  ofs4 << "  d,  \" e \"  ,f  \r\n" << std::endl;
  ofs4.close();

  std::ofstream ofs5;
  ofs5.open("test5.csv", std::ios::out | std::ios::trunc);
  ofs5 << "a,b,c\r\n" << std::endl;
  ofs5 << "de,f\r\n" << std::endl;
  ofs5.close();

  std::ofstream ofs6;
  ofs6.open("test6.csv", std::ios::out | std::ios::trunc);
  ofs6 << "a,b,c\r\n" << std::endl;
  ofs6 << "de,f" << std::endl;
  ofs6.close();

  std::ofstream ofs7;
  ofs7.open("test7.csv", std::ios::out | std::ios::trunc);
  ofs7 << "a,b,c\r\n" << std::endl;
  ofs7 << "\"de,f" << std::endl;
  ofs7.close();

  std::ofstream ofs8;
  ofs8.open("test8.csv", std::ios::out | std::ios::trunc);
  ofs8 << "a,b,c\r\n" << std::endl;
  ofs8 << "d,\" e, e \",f" << std::endl;
  ofs8.close();

  std::ofstream ofs9;
  ofs9.open("test9.csv", std::ios::out | std::ios::trunc);
  ofs9 << "a,b;b1;b2;b3,c\r\n" << std::endl;
  ofs9 << "d,e1:e-1;e2:e-2,f:f1:f2:f3" << std::endl;
  ofs9.close();

  std::ofstream ofs10;
  ofs10.open("test10.csv", std::ios::out | std::ios::trunc);
  ofs10 << "1,2,3\r\n" << std::endl;
  ofs10 << "4,5,6\r\n" << std::endl;
  ofs10.close();

  std::ofstream ofs11;
  ofs11.open("test11.csv", std::ios::out | std::ios::trunc);
  ofs11 << "1,2,3\r\n" << std::endl;
  ofs11 << "4,5,6\r\n" << std::endl;
  ofs11 << "1,2,3\r\n" << std::endl;
  ofs11 << "4,5,6\r\n" << std::endl;
  ofs11.close();

  std::ofstream ofs12;
  ofs12.open("test12.csv", std::ios::out | std::ios::trunc);
  ofs12 << "field0,field1,field2\r\n" << std::endl;
  ofs12 << "1,2,3\r\n" << std::endl;
  ofs12 << "4,5,6\r\n" << std::endl;
  ofs12 << "1,2,3\r\n" << std::endl;
  ofs12 << "4,5,6\r\n" << std::endl;
  ofs12.close();
});

CASE(TestLoad1) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == "a"));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == "c"));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == "e"));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestLoad2) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test1.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == "a"));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == "c"));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == "e"));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestLoad3) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test2.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == "a"));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == "c"));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == "e"));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestLoad4) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test3.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == "a"));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == "c"));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == "e"));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestLoad5) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test4.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == " a "));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == " c "));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == " e "));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestLoad6) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_FALSE(loader.load("test5.csv", 0));
}

CASE(TestLoad7) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_FALSE(loader.load("test6.csv", 0));
}

CASE(TestLoad8) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_FALSE(loader.load("test7.csv", 0));
}

CASE(TestLoad9) {
  kratos::util::CsvLoaderImpl loader;
  ASSERT_TRUE(loader.load("test8.csv", 0));
  const auto *row = loader.get_row(0);
  std::string value;
  ASSERT_TRUE(row->get(0, value) && (value == "a"));
  ASSERT_TRUE(row->get(1, value) && (value == "b"));
  ASSERT_TRUE(row->get(2, value) && (value == "c"));
  row = loader.get_row(1);
  ASSERT_TRUE(row->get(0, value) && (value == "d"));
  ASSERT_TRUE(row->get(1, value) && (value == " e, e "));
  ASSERT_TRUE(row->get(2, value) && (value == "f"));
}

CASE(TestReader1) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test.csv");
  std::string value;
  ASSERT_TRUE(reader->get(0, 0, value) && (value == "a"));
  ASSERT_TRUE(reader->get(0, 1, value) && (value == "b"));
  ASSERT_TRUE(reader->get(0, 2, value) && (value == "c"));
  ASSERT_TRUE(reader->get(1, 0, value) && (value == "d"));
  ASSERT_TRUE(reader->get(1, 1, value) && (value == "e"));
  ASSERT_TRUE(reader->get(1, 2, value) && (value == "f"));
  manager.stop();
}

CASE(TestReader2) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test.csv");
  std::string value;
  ASSERT_TRUE(reader->try_get(0, 0, value) && (value == "a"));
  ASSERT_TRUE(reader->try_get(0, 1, value) && (value == "b"));
  ASSERT_TRUE(reader->try_get(0, 2, value) && (value == "c"));
  ASSERT_TRUE(reader->try_get(1, 0, value) && (value == "d"));
  ASSERT_TRUE(reader->try_get(1, 1, value) && (value == "e"));
  ASSERT_TRUE(reader->try_get(1, 2, value) && (value == "f"));
  manager.stop();
}

CASE(TestReader3) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test9.csv");
  std::vector<std::string> value;
  ASSERT_TRUE(reader->try_get(0, 1, value));
  ASSERT_TRUE(value[0] == "b");
  ASSERT_TRUE(value[1] == "b1");
  ASSERT_TRUE(value[2] == "b2");
  ASSERT_TRUE(value[3] == "b3");
  ASSERT_TRUE(value.size() == 4);
  std::unordered_map<std::string, std::string> map_value;
  ASSERT_TRUE(reader->try_get(1, 1, map_value));
  ASSERT_TRUE(map_value["e1"] == "e-1");
  ASSERT_TRUE(map_value["e2"] == "e-2");
  ASSERT_TRUE(map_value.size() == 2);
  std::unordered_set<std::string> set_value;
  ASSERT_TRUE(reader->try_get(1, 2, set_value));
  ASSERT_TRUE(set_value.find("f") != set_value.end());
  ASSERT_TRUE(set_value.find("f1") != set_value.end());
  ASSERT_TRUE(set_value.find("f2") != set_value.end());
  ASSERT_TRUE(set_value.find("f3") != set_value.end());
  ASSERT_TRUE(set_value.size() == 4);
  manager.stop();
}

CASE(TestReader4) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test9.csv");
  std::vector<std::string> value;
  ASSERT_TRUE(reader->try_get(0, 1, value));
  ASSERT_TRUE(value[0] == "b");
  ASSERT_TRUE(value[1] == "b1");
  ASSERT_TRUE(value[2] == "b2");
  ASSERT_TRUE(value[3] == "b3");
  ASSERT_TRUE(value.size() == 4);
  std::map<std::string, std::string> map_value;
  ASSERT_TRUE(reader->try_get(1, 1, map_value));
  ASSERT_TRUE(map_value["e1"] == "e-1");
  ASSERT_TRUE(map_value["e2"] == "e-2");
  ASSERT_TRUE(map_value.size() == 2);
  std::set<std::string> set_value;
  ASSERT_TRUE(reader->try_get(1, 2, set_value));
  ASSERT_TRUE(set_value.find("f") != set_value.end());
  ASSERT_TRUE(set_value.find("f1") != set_value.end());
  ASSERT_TRUE(set_value.find("f2") != set_value.end());
  ASSERT_TRUE(set_value.find("f3") != set_value.end());
  ASSERT_TRUE(set_value.size() == 4);
  manager.stop();
}

CASE(TestReader5) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test.csv");
  ASSERT_TRUE(reader->create_index<std::string>(0, "field0"));
  ASSERT_TRUE(reader->create_index<std::string>(1, "field1"));
  const auto &result = reader->find({{"field0", "d"}, {"field1", "e"}});
  const auto &cursor = result.get_cursor();
  std::string value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value == "d"));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == "e"));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == "f"));
  manager.stop();
}

CASE(TestReader6) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test.csv");
  ASSERT_TRUE(reader->create_index<std::string>(0, "field0"));
  ASSERT_TRUE(reader->create_index<std::string>(1, "field1"));
  const auto &result = reader->find({{"field0", "123"}, {"field1", "e"}});
  const auto &cursor = result.get_cursor();
  ASSERT_FALSE(cursor);
  manager.stop();
}

CASE(TestReader7) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test10.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({{"field0", 1}, {"field1", 2}});
  const auto &cursor = result.get_cursor();
  std::string value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value == "1"));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == "2"));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == "3"));
  manager.stop();
}

CASE(TestReader8) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test10.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1, 2});
  const auto &cursor = result.get_cursor();
  std::string value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value == "1"));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == "2"));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == "3"));
  manager.stop();
}

CASE(TestReader9) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test10.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1});
  const auto &cursor = result.get_cursor();
  std::string value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value == "1"));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == "2"));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == "3"));
  manager.stop();
}

CASE(TestReader10) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test11.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1});
  const auto &cursor = result.get_cursor();
  int value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value == 1));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == 2));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == 3));
  cursor.next();
  ASSERT_TRUE(cursor.try_get(0, value) && (value == 1));
  ASSERT_TRUE(cursor.try_get(1, value) && (value == 2));
  ASSERT_TRUE(cursor.try_get(2, value) && (value == 3));
  manager.stop();
}

CASE(TestReader11) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test11.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1});
  const auto &cursor = result.get_cursor();
  std::vector<int> value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value[0] == 1));
  ASSERT_TRUE(cursor.try_get(1, value) && (value[0] == 2));
  ASSERT_TRUE(cursor.try_get(2, value) && (value[0] == 3));
  cursor.next();
  ASSERT_TRUE(cursor.try_get(0, value) && (value[0] == 1));
  ASSERT_TRUE(cursor.try_get(1, value) && (value[0] == 2));
  ASSERT_TRUE(cursor.try_get(2, value) && (value[0] == 3));
  manager.stop();
}

CASE(TestReader12) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test11.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1});
  const auto &cursor = result.get_cursor();
  std::set<int> value;
  ASSERT_TRUE(cursor.try_get(0, value) && (value.find(1) != value.end()));
  ASSERT_TRUE(cursor.try_get(1, value) && (value.find(2) != value.end()));
  ASSERT_TRUE(cursor.try_get(2, value) && (value.find(3) != value.end()));
  cursor.next();
  ASSERT_TRUE(cursor.try_get(0, value) && (value.find(1) != value.end()));
  ASSERT_TRUE(cursor.try_get(1, value) && (value.find(2) != value.end()));
  ASSERT_TRUE(cursor.try_get(2, value) && (value.find(3) != value.end()));
  manager.stop();
}

CASE(TestReader13) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test11.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({1});
  const auto &cursor = result.get_cursor();
  std::unordered_map<int, int> value;
  ASSERT_TRUE(!cursor.try_get(0, value) && (value.find(1) == value.end()));
  ASSERT_TRUE(!cursor.try_get(1, value) && (value.find(2) == value.end()));
  ASSERT_TRUE(!cursor.try_get(2, value) && (value.find(3) == value.end()));
  cursor.next();
  ASSERT_TRUE(!cursor.try_get(0, value) && (value.find(1) == value.end()));
  ASSERT_TRUE(!cursor.try_get(1, value) && (value.find(2) == value.end()));
  ASSERT_TRUE(!cursor.try_get(2, value) && (value.find(3) == value.end()));
  manager.stop();
}

CASE(TestReader14) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test11.csv");
  ASSERT_TRUE(reader->create_index<int>(0, "field0"));
  ASSERT_TRUE(reader->create_index<int>(1, "field1"));
  const auto &result = reader->find({100});
  const auto &cursor = result.get_cursor();
  ASSERT_FALSE(result);
  ASSERT_FALSE(cursor);
  int value;
  ASSERT_FALSE(cursor.try_get(0, value));
  manager.stop();
}

CASE(TestReader15) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test9.csv");
  ASSERT_TRUE(reader->create_index<std::string>(0, "field0"));
  ASSERT_TRUE(reader->create_index<std::string>(1, "field1"));
  const auto &result = reader->find({"d"});
  const auto &cursor = result.get_cursor();
  std::unordered_map<std::string, std::string> value;
  cursor.try_get(1, value);
  ASSERT_TRUE(value["e1"] == "e-1");
  ASSERT_TRUE(value["e2"] == "e-2");
  std::unordered_set<std::string> value_un_set;
  cursor.try_get(2, value_un_set);
  ASSERT_TRUE(value_un_set.find("f") != value_un_set.end());
  ASSERT_TRUE(value_un_set.find("f1") != value_un_set.end());
  ASSERT_TRUE(value_un_set.find("f2") != value_un_set.end());
  ASSERT_TRUE(value_un_set.find("f3") != value_un_set.end());
  manager.stop();
}

CASE(TestReader16) {
  kratos::util::CsvManagerImpl manager(nullptr);
  manager.set_root_directory(".");
  manager.ignore("test5.csv");
  manager.ignore("test6.csv");
  manager.ignore("test7.csv");
  ASSERT_TRUE(manager.start());
  auto reader = manager.new_reader("test10.csv");
  const auto &result = reader->find_all();
  const auto &cursor = result.get_cursor();
  ASSERT_TRUE(result);
  ASSERT_TRUE(cursor);
  int count = 0;
  while (cursor.has_next()) {
      int value;
      ASSERT_TRUE(cursor.try_get(0, value));
      ASSERT_TRUE(cursor.try_get(1, value));
      ASSERT_TRUE(cursor.try_get(2, value));
      count += 1;
      cursor.next();
  }
  ASSERT_TRUE(count == 2);
  count = 0;
  cursor.reset();
  while (cursor.has_next()) {
      if (count == 0) {
          ASSERT_TRUE(cursor.at<int>(0) == 1);
          ASSERT_TRUE(cursor.at<int>(1) == 2);
          ASSERT_TRUE(cursor.at<int>(2) == 3);
      } else {
          ASSERT_TRUE(cursor.at<int>(0) == 4);
          ASSERT_TRUE(cursor.at<int>(1) == 5);
          ASSERT_TRUE(cursor.at<int>(2) == 6);
      }
      count += 1;
      cursor.next();
  }
  ASSERT_EXCEPT(cursor.at<int>(10));
  manager.stop();
}

CASE(TestReader17) {
    kratos::util::CsvManagerImpl manager(nullptr);
    manager.set_root_directory(".");
    manager.ignore("test5.csv");
    manager.ignore("test6.csv");
    manager.ignore("test7.csv");
    ASSERT_TRUE(manager.start());
    auto reader = manager.new_reader("test10.csv", { {"field0", 0}, {"field1", 1}, {"field2", 2} });
    const auto& result = reader->find_all();
    const auto& cursor = result.get_cursor();
    ASSERT_TRUE(result);
    ASSERT_TRUE(cursor);
    int count = 0;
    while (cursor.has_next()) {
        int value;
        ASSERT_TRUE(cursor.try_get(0, value));
        ASSERT_TRUE(cursor.try_get(1, value));
        ASSERT_TRUE(cursor.try_get(2, value));
        count += 1;
        cursor.next();
    }
    ASSERT_TRUE(count == 2);
    count = 0;
    cursor.reset();
    while (cursor.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor.at<int>(0) == 1);
            ASSERT_TRUE(cursor.at<int>(1) == 2);
            ASSERT_TRUE(cursor.at<int>(2) == 3);
        }
        else {
            ASSERT_TRUE(cursor.at<int>(0) == 4);
            ASSERT_TRUE(cursor.at<int>(1) == 5);
            ASSERT_TRUE(cursor.at<int>(2) == 6);
        }
        count += 1;
        cursor.next();
    }
    ASSERT_EXCEPT(cursor.at<int>(10));
    count = 0;
    cursor.reset();
    while (cursor.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor.at<int>("field0") == 1);
            ASSERT_TRUE(cursor.at<int>("field1") == 2);
            ASSERT_TRUE(cursor.at<int>("field2") == 3);
        }
        else {
            ASSERT_TRUE(cursor.at<int>("field0") == 4);
            ASSERT_TRUE(cursor.at<int>("field1") == 5);
            ASSERT_TRUE(cursor.at<int>("field2") == 6);
        }
        count += 1;
        cursor.next();
    }
    ASSERT_EXCEPT(cursor.at<int>("unknown"));
    manager.stop();
}

CASE(TestReader18) {
    kratos::util::CsvManagerImpl manager(nullptr);
    manager.set_root_directory(".");
    manager.ignore("test5.csv");
    manager.ignore("test6.csv");
    manager.ignore("test7.csv");
    ASSERT_TRUE(manager.start());
    auto reader = manager.new_reader("test12.csv", true);
    const auto& result = reader->find_all();
    const auto& cursor = result.get_cursor();
    ASSERT_TRUE(result);
    ASSERT_TRUE(cursor);
    int count = 0;
    while (cursor.has_next()) {
        int value;
        ASSERT_TRUE(cursor.try_get(0, value));
        ASSERT_TRUE(cursor.try_get(1, value));
        ASSERT_TRUE(cursor.try_get(2, value));
        count += 1;
        cursor.next();
    }
    ASSERT_TRUE(count == 4);
    count = 0;
    cursor.reset();
    while (cursor.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor.at<int>(0) == 1);
            ASSERT_TRUE(cursor.at<int>(1) == 2);
            ASSERT_TRUE(cursor.at<int>(2) == 3);
        }
        else {
            ASSERT_TRUE(cursor.at<int>(0) == 4);
            ASSERT_TRUE(cursor.at<int>(1) == 5);
            ASSERT_TRUE(cursor.at<int>(2) == 6);
            break;
        }
        count += 1;
        cursor.next();
    }
    ASSERT_EXCEPT(cursor.at<int>(10));
    count = 0;
    cursor.reset();
    while (cursor.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor.at<int>("field0") == 1);
            ASSERT_TRUE(cursor.at<int>("field1") == 2);
            ASSERT_TRUE(cursor.at<int>("field2") == 3);
        }
        else {
            ASSERT_TRUE(cursor.at<int>("field0") == 4);
            ASSERT_TRUE(cursor.at<int>("field1") == 5);
            ASSERT_TRUE(cursor.at<int>("field2") == 6);
            break;
        }
        count += 1;
        cursor.next();
    }
    ASSERT_EXCEPT(cursor.at<int>("unknown"));
    manager.stop();
}

CASE(TestReaderLast) {
    kratos::util::CsvManagerImpl manager(nullptr);
    manager.set_root_directory(".");
    manager.ignore("test5.csv");
    manager.ignore("test6.csv");
    manager.ignore("test7.csv");
    ASSERT_TRUE(manager.start(1));
    auto reader = manager.new_reader("test11.csv");

    const auto& result = reader->find_all();
    const auto& cursor = result.get_cursor();
    ASSERT_TRUE(result);
    ASSERT_TRUE(cursor);
    int count = 0;
    while (cursor.has_next()) {
        int value;
        ASSERT_TRUE(cursor.try_get(0, value));
        ASSERT_TRUE(cursor.try_get(1, value));
        ASSERT_TRUE(cursor.try_get(2, value));
        count += 1;
        cursor.next();
    }
    ASSERT_TRUE(count == 4);
    count = 0;
    cursor.reset();
    while (cursor.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor.at<int>(0) == 1);
            ASSERT_TRUE(cursor.at<int>(1) == 2);
            ASSERT_TRUE(cursor.at<int>(2) == 3);
        }
        else {
            ASSERT_TRUE(cursor.at<int>(0) == 4);
            ASSERT_TRUE(cursor.at<int>(1) == 5);
            ASSERT_TRUE(cursor.at<int>(2) == 6);
            break;
        }
        count += 1;
        cursor.next();
    }
    // 改变test10.csv的内容
    std::ofstream ofs;
    ofs.open("test11.csv", std::ios::out | std::ios::trunc);
    ofs << "4,5,6\r\n" << std::endl;
    ofs << "1,2,3\r\n" << std::endl;
    ofs << "4,5,6\r\n" << std::endl;
    ofs << "1,2,3\r\n" << std::endl;
    ofs.close();
    // 等待热更新
    auto start = kratos::util::get_os_time_millionsecond();
    while (true) {
        manager.update();
        if (kratos::util::get_os_time_millionsecond() - start > 3000) {
            break;
        }
    }
    const auto& result_new = reader->find_all();
    const auto& cursor_new = result_new.get_cursor();
    ASSERT_TRUE(result_new);
    ASSERT_TRUE(cursor_new);
    count = 0;
    while (cursor_new.has_next()) {
        int value;
        ASSERT_TRUE(cursor_new.try_get(0, value));
        ASSERT_TRUE(cursor_new.try_get(1, value));
        ASSERT_TRUE(cursor_new.try_get(2, value));
        count += 1;
        cursor_new.next();
    }
    ASSERT_TRUE(count == 4);
    count = 0;
    cursor_new.reset();
    while (cursor_new.has_next()) {
        if (count == 0) {
            ASSERT_TRUE(cursor_new.at<int>(0) == 4);
            ASSERT_TRUE(cursor_new.at<int>(1) == 5);
            ASSERT_TRUE(cursor_new.at<int>(2) == 6);
        }
        else {
            ASSERT_TRUE(cursor_new.at<int>(0) == 1);
            ASSERT_TRUE(cursor_new.at<int>(1) == 2);
            ASSERT_TRUE(cursor_new.at<int>(2) == 3);
            break;
        }
        count += 1;
        cursor_new.next();
    }
    ASSERT_EXCEPT(cursor_new.at<int>(10));
    manager.stop();
}

TEARDOWN([]() {
  kratos::util::remove_file("test.csv");
  kratos::util::remove_file("test1.csv");
  kratos::util::remove_file("test2.csv");
  kratos::util::remove_file("test3.csv");
  kratos::util::remove_file("test4.csv");
  kratos::util::remove_file("test5.csv");
  kratos::util::remove_file("test6.csv");
  kratos::util::remove_file("test7.csv");
  kratos::util::remove_file("test8.csv");
  kratos::util::remove_file("test9.csv");
  kratos::util::remove_file("test10.csv");
  kratos::util::remove_file("test11.csv");
  kratos::util::remove_file("test12.csv");
});

FIXTURE_END(test_lang)
